#include "stdafx.h"
#include "hwincommandline.h"
#include "hwinexception.h"
#include "hwinenvironment.h"
#include "hwinio.h"

namespace harlinn
{
    namespace windows
    {
        using namespace io;

        HWIN_EXPORT Commandline::Commandline()
            : options(CommandlineOptions::Default)
        {}

        HWIN_EXPORT Commandline::Commandline(const String& theCommandline, CommandlineOptions theCommandlineOptions)
            : commandline(theCommandline),options(theCommandlineOptions)
        {}

        HWIN_EXPORT Commandline::Commandline(const wchar_t* theCommandline, CommandlineOptions theCommandlineOptions)
            : commandline(theCommandline),options(theCommandlineOptions)
        {}

        HWIN_EXPORT Commandline::Commandline(const wchar_t* theCommandline, size_t theCommandlineLength, CommandlineOptions theCommandlineOptions)
            : commandline(theCommandline,theCommandlineLength),options(theCommandlineOptions)
        {}


        void Commandline::Parse()
        {
            Base::clear();
            command = String();
            auto length = commandline.length();
            
            if(length)
            {
                const wchar_t *start = commandline.c_str();
                const wchar_t *end = start + length;

                while(start < end && (iswspace(*start) || *start < L'\x20' ))
                {
                    start++;
                }

                if(start < end)
                {
                    end--;
                    while(iswspace(*end) || *end < L'\x20')
                    {
                        end--;
                    }
                    end++;
                    if( (options & CommandlineOptions::FirstIsCommand) == CommandlineOptions::FirstIsCommand)
                    {
                        start = ParseFirst(start,end);
                    }
                    while(start < end)
                    {
                        start = ParseArgument(start,end);
                    }
                }
            }

        }

        const wchar_t* Commandline::ParseFirst(const wchar_t* start, const wchar_t* end)
        {
            String s;
            bool inQuote = false;
            const wchar_t* p = start;
            
            // Eat whitespace
            while(p < end && (iswspace(*p) || *p < L'\x20' ))
            {
                p++;
            }
            while(p < end)
            {
                wchar_t c = *p;
                
                if(c == L'"')
                {
                    if(inQuote)
                    {
                        if( *(p-1) != L'"' )
                        {
                            inQuote = false;
                        }
                        else 
                        {
                            s+= c;
                        }
                    }
                    else
                    {
                        inQuote = true;
                    }
                }
                else
                {
                    if(iswspace(c))
                    {
                        if(inQuote)
                        {
                            s+= c;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        s+= c;
                    }
                }
                p++;
            }
            if( (options & CommandlineOptions::ExpandEnvironmentVariables) == CommandlineOptions::ExpandEnvironmentVariables)
            {
                command = Environment::Expand( s );
            }
            else
            {
                command = s;
            }
            return p;
        }

        const wchar_t* Commandline::ParseArgument(const wchar_t* start, const wchar_t* end)
        {
            String s;
            bool inQuote = false;
            const wchar_t* p = start;
            
            // Eat whitespace
            while(p < end && (iswspace(*p) || *p < L'\x20' ))
            {
                p++;
            }
            while(p < end)
            {
                wchar_t c = *p;
                
                if(c == L'"')
                {
                    if(inQuote)
                    {
                        if( *(p-1) != L'"' )
                        {
                            inQuote = false;
                        }
                        else 
                        {
                            s+= c;
                        }
                    }
                    else
                    {
                        inQuote = true;
                    }
                }
                else
                {
                    if(iswspace(c))
                    {
                        if(inQuote)
                        {
                            s+= c;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        s+= c;
                    }
                }
                p++;
            }
            
            if( (options & CommandlineOptions::ExpandEnvironmentVariables) == CommandlineOptions::ExpandEnvironmentVariables)
            {
                s = Environment::Expand( s );
            }

            if(( (options & CommandlineOptions::ExpandWildCards) == CommandlineOptions::ExpandWildCards) && 
                (s.IndexOfAnyOf(L"?*") != String::npos) && s.StartsWith(L"/?") == false )
            {
                Expand(s);
            }
            else
            {
                push_back(s);
            }

            return p;
        }

        void Commandline::Expand(const String& theArgument)
        {
            FileSystemEntriesBase entries(theArgument);
            if(entries.Read())
            {
                do
                {
                    push_back(entries.FullPath());
                }while(entries.Read());
            }
            else
            {
                push_back(theArgument);
            }
        }

    }
}